<template>
  <div>
    <div>
      <el-input style="width: 200px" placeholder="清单名称(选填)" v-model="workNameSearch"></el-input>
      <el-select v-model="creatorSearch" filterable placeholder="选择创建人" style="width: 150px; margin: 0 5px">
        <el-option
            v-for="item in userOptions"
            :key="item.value"
            :label="item.label"
            :value="item.value">
        </el-option>
      </el-select>
      <el-input style="width: 200px; margin: 0 5px" placeholder="文件名称(选填)" v-model="attachNameSearch"></el-input>
      <el-button type="primary" @click="load(1)">查询</el-button>
      <el-button type="success" @click="reset">刷新</el-button>
    </div>

    <div style="margin: 10px 0">
      <el-button type="primary" plain @click="handleAddOrEdit">新建清单</el-button>
      <el-button type="danger" plain @click="handleDelBatch">批量删除</el-button>
      <el-button type="info" plain @click="downloadExcelTemplate" style="margin-left: 10px">下载Excel清单筛选模板</el-button>
      <el-button type="warning" plain @click="showAutocodeDialog">编码小程序</el-button>
      <el-button type="success" plain @click="showWsChatDialog">发站内信</el-button>
    </div>
    <!--  整个表格，有多选框 -->
    <el-table v-loading="pageLoading" element-loading-text="拼命加载中" :data="tableData" stripe :header-cell-style="{backgroundColor: 'aliceblue',color: '#666'}"
    @selection-change="handleSelectionChange">
  <!--    复选框-->
      <el-table-column type="selection" width="55" align="center"></el-table-column>
      <el-table-column prop="id" label="ID" width="80" align="center"></el-table-column>
      <el-table-column prop="workName" label="任务名称" width="100"></el-table-column>
      <el-table-column prop="workDescription" label="任务描述" ></el-table-column>
      <el-table-column prop="attachName" label="文件名称" width="140px" show-overflow-tooltip></el-table-column>
      <el-table-column prop="attachCount" label="文件个数" width="80px"></el-table-column>
      <el-table-column prop="creatorName" label="创建人" width="80px"></el-table-column>
      <el-table-column prop="creatTime" label="创建时间" width="140px"></el-table-column>
      <el-table-column prop="state" label="加工状态" width="150">
        <template v-slot="scope">
          <el-tag :type="getStatusType(scope.row.state)">{{getStatusText(scope.row.state)}}</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="takeTime" label="加工耗时" width="140px"></el-table-column>
      <el-table-column prop="remark" label="备注"></el-table-column>

      <el-table-column fixed="right" label="操作" align="center" width="240px">
        <template v-slot="scope">
          <el-button v-if="isRecipient(scope.row)" size="mini" type="primary" plain 
                  @click="downLoadProcess(scope.row)" 
                  :loading="downloadLoading[scope.row.id]">下载</el-button>
          <!-- 优化进度条显示逻辑：当进度100%时显示成功状态，否则显示过程状态 -->
          <el-progress 
            v-if="downloadProgress[scope.row.id] > 0 || downloadLoading[scope.row.id]"
            :percentage="downloadProgress[scope.row.id]" 
            :status="getProgressStatus(scope.row.id)"
            style="margin-top: 5px;"
          ></el-progress>
          <el-button size="mini" type="danger" plain @click="handleDel(scope.row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <div style="margin: 10px 0;text-align: center">
      <el-pagination @current-change="handleCurrentChange"
                    :current-page="pageNum"
                    :page-size="pageSize"
                    layout="total,prev,pager,next"
                    :total="total">
      </el-pagination>
    </div>
    <!-- 新增表单需要弹窗包裹 -->
    <el-dialog title="新建清单"
              :visible.sync="formVisible"
              width="40%"
              @close="closeFormVisible" >
      <el-form :model="form" label-width="80px" style="padding-right: 20px" :rules="rules" ref="formRef">
        <el-row>
          <el-col :span="12">
            <el-form-item label="清单名称" prop="workName">
              <el-input v-model="form.workName" placeholder="清单名称(必填)"></el-input>
            </el-form-item>
          </el-col>
          <el-col :span="6">
            <!-- 添加自动处理物料编码复选框 -->
            <el-form-item>
              <el-checkbox v-model="form.autoCode">自动处理物料编码</el-checkbox>
            </el-form-item>
          </el-col>
          <el-col :span="6">
            <el-form-item>
              <div style="display: block;">
                <el-radio-group v-model="form.deWeightType" style="display: block;">
                  <el-tooltip placement="top" content="按项目去重：保留不同项目下的相同零件" effect="dark" trigger="hover">
                    <el-radio label="project" style="display: block; margin-bottom: 8px; cursor: pointer;">按项目去重</el-radio>
                  </el-tooltip>
                  <el-tooltip placement="top" content="按零件去重：所有相同零件只保留一个" effect="dark" trigger="hover">
                    <el-radio label="part" style="display: block; cursor: pointer;">按零件去重</el-radio>
                  </el-tooltip>
                </el-radio-group>
              </div>
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="清单描述" prop="workDescription">
          <el-input v-model="form.workDescription" placeholder="描述(可不填)" type="textarea"></el-input>
        </el-form-item>

        <el-form-item label="附件名称" prop="attachName">
          <el-input v-model="form.attachName" placeholder="【系统自动填写】显示批量导入Bom文件的名称" :readonly="true"></el-input>
          <div style="margin: 15px;text-align: center">
            <!-- :on-change="handleChange" -->
            <el-upload ref="uploader"
                class="upload-demo"
                :action= "this.$apiUrl+'/file/upload'"
                :headers="{token: user.token}"
                :file-list="fileList"
                :on-preview="handlePreview"
                :on-success="handleOnSuccess"
                :before-upload="beforeUpload"
                :before-remove="beforeRemoved"
                multiple
                :limit="30"
                :on-exceed="handleExceed"
                :drag="true"
                @drop="handleDrop"
                @dragover.prevent
            >
              <div class="el-upload__tip">将文件拖到此处，或<em>点击上传</em></div>
              <div slot="tip" class="el-upload__tip">注意：单个文件安全范围是100MB以内</div>
            </el-upload>
          </div>
        </el-form-item>

        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" placeholder="备注(可不填)" type="textarea"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="formVisible = false">取消</el-button>
        <el-button type="primary" @click="save" :loading="saveLoad">提交</el-button>
      </div>
    </el-dialog>

    <el-dialog :visible.sync="autocodeVisible" :close-on-click-modal="false" title="自动编码小程序-初始版(v0.0.1)">
      <Autocode />
    </el-dialog>

    <el-dialog :visible.sync="wsDialogVisible" title="发送站内信" width="80%" @close="closeFormVisible">
      <WsChat/>
    </el-dialog>
  </div>
</template>

<script>
  import Autocode from '@/views/manager/utils/Autocode.vue';
  import WsChat from '@/views/manager/utils/WsChat.vue';
  // import E from 'wangeditor'
  // import hljs from 'highlight.js'
  export default {
    name: "Bom",
    inject: { 
      getWebsocket: { 
        default: () => () => null 
      } 
    }, // 获取父组件提供的websocket获取函数
    data(){
      return {
        tableData: []//表数据
        ,pageNum: 1//当前页码
        ,pageSize: 10//每页数量
        ,workNameSearch: ''
        ,creatorSearch: ''
        ,wsOppo: ''
        ,attachNameSearch: ''
        ,total: 0 //数据总条数
        ,user: JSON.parse(localStorage.getItem('userInfo') || '{}')
        ,ids: []
        ,formVisible: false //默认不显示新增表单弹窗
        ,form: {
          autoCode: true, // 是否自动处理物料编码
          deWeightType: 'project' // 去重类型: project-按项目去重, part-按零件去重
        } //弹窗表单
        ,originalOnMessage: null // 保存原始的onmessage处理函数
        ,rules: {
          //校验规则
          workName: [
            {
              required: true,
              message: '选择任务名称，带 * 是必填项',
              trigger: 'blur',
            }
          ]
        }
        ,fileList: []
        ,selectVal: "", // select框的绑定值
        selectName: "", // select框显示的name
        // 树形控件数据
        treeData: [],
        defaultProps: {
          children: "children",
          label: "name",
        },
        // 标签数组
        tagList: [
          { value: "admin", label: "管理员" },
          { value: "user", label: "用户" },
          { value: "test", label: "测试员" },
        ]
        ,userOptions: []
        ,userOptionsLoading: false
        ,saveLoad: false
        ,downloadLoading: {} // 改为对象存储每行下载加载状态（key为row.id）
        ,downloadProgress: {} // 改为对象存储每行下载进度（key为row.id）
        ,pageLoading: false // 添加加载状态变量1
        ,autocodeVisible: false // 添加自动编码弹窗的显示状态
        ,wsDialogVisible: false //站内信弹窗
        ,msgContent: '' //测试输入内容
        ,msgList: [] //消息列表：发送或接收记录
        ,wsLoading: false // 聊天记录加载状态
        ,editor: null //富文本编辑框
        // ,wsForm: { // 站内信表单数据
        //   wsOppo: '',
        //   content: ''
        // }
        // ,wsRules: { // 站内信表单规则
        //   wsOppo: [
        //     {
        //       required: true,
        //       message: '请选择会话人',
        //       trigger: 'change'
        //     }
        //   ],
        //   content: [
        //     {
        //       required: true,
        //       message: '消息内容不能为空',
        //       trigger: 'blur'
        //     }
        //   ],
        // },
        // ,showTime: {}// 使用对象存储每个消息的时间显示状态
        ,timers: {} // 使用对象存储每个消息的定时器
      }
    },
    created() {
      this.pageLoading = true;
      this.load();
    },
    //安装
    mounted() {
      this.initUsersOptions();
      this.initWebSocketListener();
    },
    //vue销毁之前
    beforeDestroy() {
      // 清理WebSocket事件监听器
      const websocket = this.getWebsocket();
      if (websocket) {
        try {
          // 尝试恢复原始的onmessage处理函数
          if (this.originalOnMessage) {
            websocket.onmessage = this.originalOnMessage;
          } else {
            // 如果没有保存原始处理函数，设置为null
            websocket.onmessage = null;
          }
        } catch (error) {
          console.error('清理WebSocket事件监听器失败:', error);
        }
      }
      // 清理所有定时器
      if (this.timers && typeof this.timers === 'object') {
        Object.values(this.timers).forEach(timer => {
          if (timer) {
            try {
              clearTimeout(timer);
            } catch (error) {
              console.error('清理定时器失败:', error);
            }
          }
        });
      }
      this.timers = {};
    },
    computed: {

    },
    components: {
      Autocode,
      WsChat
    },
    watch: {
      // // 监听会话人变化，清空聊天记录
      // 'wsForm.wsOppo'(newVal, oldVal) {
      //   if (newVal !== oldVal) {
      //     // 清空旧记录
      //     this.msgList = [];
      //     if (newVal) { // 新会话人有效时请求数据
      //       this.wsLoading = true;
      //       this.$request.get('/chat/getChatRecords', {
      //         params: { 
      //           userId: this.user.id,
      //           oppoId: newVal 
      //         }
      //       }).then(res => {
      //         if (res.code === '200') {
      //           this.msgList = res.data.map(item => ({
      //             id: item.id, // 消息ID
      //             type: item.userId === this.user.id.toString() ? 'me' : 'oppo',
      //             content: item.content,
      //             time: item.creatTime, // 假设需要格式化时间
      //             isRead: item.userId === this.user.id.toString() ? true : item.isRead || false // 自己发送的消息默认为已读，收到的消息根据服务器状态或默认为未读
      //           }));
      //         } else {
      //           this.$message.error('获取聊天记录失败：' + res.msg);
      //         }
      //       }).catch(error => {
      //         this.$message.error('网络错误，获取聊天记录失败');
      //       }).finally(() => {
      //         this.wsLoading = false;
      //       });
      //     }
      //   }
      // }
    },
    methods: {
      // 初始化WebSocket事件监听
      initWebSocketListener() {
        try {
          const websocket = this.getWebsocket();
          if (!websocket) {
            console.warn('WebSocket实例不存在，无法初始化事件监听');
            return;
          }
          
          // 保存原始的onmessage处理函数
          this.originalOnMessage = websocket.onmessage;
          
          // 创建新的onmessage处理函数
          const newOnMessage = (event) => {
            try {
              const messageData = JSON.parse(event.data);
              // 检查消息类型是否为success或fail
              if (messageData.type in ['success','fail']) {
                console.log('收到消息:', messageData.content);
                // 调用reset方法刷新列表
                this.reset();
              }
            } catch (error) {
              console.error('解析WebSocket消息失败:', error);
            }
            
            // 调用原始的onmessage处理函数，确保不影响现有功能
            if (typeof this.originalOnMessage === 'function') {
              this.originalOnMessage(event);
            }
          };
          
          // 绑定this上下文
          websocket.onmessage = newOnMessage.bind(this);
        } catch (error) {
          console.error('初始化WebSocket事件监听失败:', error);
        }
      },
      
      // handleMouseEnter(index) {
      //   // 为每个消息项设置独立的定时器
      //   this.clearTimer(index);
      //   this.timers[index] = setTimeout(() => {
      //     this.$set(this.showTime, index, true);
      //   }, 3000);
      // },
      // handleMouseLeave(index) {
      //   this.clearTimer(index);
      //   this.$set(this.showTime, index, false);
      // },
      // // 标记消息为已读
      // markAsRead(index) {
      //   if (this.msgList[index].type === 'oppo' && !this.msgList[index].isRead) {
      //     this.$set(this.msgList[index], 'isRead', true);
      //     // 发送已读状态到服务器
      //     this.sendReadStatusToServer(this.msgList[index]);
      //   }
      // },
      // 发送初始化消息
      sendInitMsg() {
        try {
          const websocket = this.getWebsocket();
          if (websocket && websocket.readyState === WebSocket.OPEN) {
            const initMsg = {
              type: 'init',
              content: '连接测试',
              userId: this.user?.id || '',
              time: new Date().toISOString()
            };
            websocket.send(JSON.stringify(initMsg));
          } else {
            console.warn('WebSocket未连接，无法发送初始化消息');
          }
        } catch (error) {
          console.error('发送初始化消息失败:', error);
        }
      },
      // clearTimer(index) {
      //   if (this.timers[index]) {
      //     clearTimeout(this.timers[index]);
      //     this.timers[index] = null;
      //   }
      // },
      checkWs(websocket){
        if (!websocket) {
          this.$message.error('校验WS失败：实例不存在');
          return false;
        }
        if (websocket.readyState !== WebSocket.OPEN) {
          this.$message.warning('校验WS失败：当前状态不是OPEN，而是' + websocket.readyState);
          return false;
        }
      },
      //弹窗打开后，先显示连接消息，测试是否通畅
      wsInit(){        
        const websocket = this.getWebsocket();
        if (!websocket) {
          console.warn('WebSocket实例不存在');
          return;
        }
        
        // 保存当前的onmessage处理函数（可能是initWebSocketListener中设置的）
        const currentOnMessage = websocket.onmessage;
        
        this.sendInitMsg();
        
        // 创建新的onmessage处理函数，保留之前的处理逻辑
        websocket.onmessage = (event) => {
          try {
            const msg = JSON.parse(event.data);
            
            // 处理站内信相关消息
            if (msg.type === 'init') {
              this.msgList.push({
                id: msg.id, // 消息ID
                type: msg.type, 
                content: msg.content, 
                time: new Date().toLocaleString(),
                read: msg.type === 'me' ? true : false
              });
              this.scrollToBottom();
            } else if(msg.type === 'chat') {
              // 站内信聊天消息处理
              this.msgList.push({ 
                id: msg.id, // 消息ID
                type: 'oppo', 
                content: msg.content, 
                time: new Date().toLocaleString(),
                read: false
              });
              this.scrollToBottom();
            } else if(['success', 'warning', 'info', 'error'].includes(msg.type)) {
              // 普通通知类型
              this.$notify({
                title: '收到通知',
                message: msg.content,
                type: msg.type,
                duration: 3000
              });
            }
          } catch (error) {
            console.error('处理站内信WebSocket消息失败:', error);
          }
          
          // 调用之前保存的处理函数，确保不影响现有功能
          if (typeof currentOnMessage === 'function') {
            try {
              currentOnMessage(event);
            } catch (error) {
              console.error('调用原有WebSocket处理函数失败:', error);
            }
          }
        };
      },
      initUsersOptions() {
        this.userOptionsLoading = true;
        this.$request.get('/user/options').then(res => {
          if (res.code === '200') {
            this.userOptions = res.data;
          } else {
            this.$message.error('获取用户选项失败');
            this.userOptions = [];
          }
        }).catch(() => {
          this.$message.error('网络错误，获取用户选项失败');
          this.userOptions = [];
        }).finally(() => {
          this.userOptionsLoading = false;
        });
      },
      getProgressStatus(id) {
        const progress = this.downloadProgress[id];
        if (typeof progress === 'undefined') return undefined;
        if (progress === 100) return 'success';
        if (progress === -1) return 'exception';
        return undefined; // 保持默认状态;
      },
      reset(){
        this.pageLoading = true;
        this.workNameSearch = ''
        this.creatorSearch = ''
        this.attachNameSearch = ''
        this.load()
      },
      load(pageNum){
        if (pageNum) this.pageNum = pageNum;  //分页查询时自动切换到第一页
        this.$request.get('/bom/findByPage',{
          params:{
            pageNum: this.pageNum,
            pageSize: this.pageSize,
            workName: this.workNameSearch,
            creator: this.creatorSearch,
            attachName: this.attachNameSearch
          }
        }).then(res => {
          if (res.code === "200"){
            if (res.data){
              if (res.data.records){
                this.tableData = res.data.records
              }else {
                this.tableData = res.data
              }
            }
            this.total = res.data.total
          }else {
            this.$message.error(res.msg)
          }
          this.pageLoading = false
        })
      },
      //切换页码
      handleCurrentChange(pageNum){
        this.load(pageNum)
      },
      /**
       * 优化后的下载方法
       */
      downLoadProcess(row) {
        const rowId = row.id;
        // 初始化行级状态（已移除冗余的对象初始化检查）
        this.$set(this.downloadLoading, rowId, true);
        this.$set(this.downloadProgress, rowId, 0);
        
        this.$message.info('文件下载开始...');
        const url = `${this.$apiUrl}/bom/download`;
        const params = { id: rowId, type: 'attachment', token: this.user.token };
        const fullUrl = `${url}?${new URLSearchParams(params).toString()}`;
        
        // 使用Vue响应式方式管理定时器
        const intervalId = setInterval(() => {
          if (this.downloadProgress[rowId] < 90) {
            this.$set(this.downloadProgress, rowId, this.downloadProgress[rowId] + 10);
          }
        }, 1000);
        
        fetch(fullUrl, { 
          headers: { 
            'Authorization': `Bearer ${this.user.token}`,
            'Cache-Control': 'no-cache' // 添加缓存控制
          } 
        })
        .then(response => {
          if (!response.ok) throw new Error(`HTTP错误: ${response.status}`);
          return response.blob().then(blob => ({ 
            blob,
            fileName: this.parseFileName(response.headers) 
          }));
        })
        .then(({ blob, fileName }) => {
          this.triggerFileDownload(blob, fileName);
          this.$set(this.downloadProgress, rowId, 100); // 强制完成进度
          this.$message.success('文件下载成功');
        })
        .catch(error => {
          console.error('下载失败:', error);
          this.$message.error('文件下载失败，请联系管理员');
          this.$set(this.downloadProgress, rowId, 0); // 失败时立即重置
        })
        .finally(() => {
          clearInterval(intervalId);
          setTimeout(() => {
            this.$set(this.downloadLoading, rowId, false);
            this.$set(this.downloadProgress, rowId, 0);
          }, 500); // 延长动画展示时间
        });
      },
      
      // 新增辅助方法
      parseFileName(headers) {
        const defaultName = '未命名文件';
        const disposition = headers.get('Content-Disposition') || defaultName;
        try{
          const matches = disposition.match(/filename\*?=(?:UTF-8'')?([^;]+)/i);
          if (matches) return decodeURIComponent(matches[1]);
          // 处理常规文件名
          const filenameMatch = disposition.match(/filename="?([^"]+)"?/i);
          return filenameMatch ? filenameMatch[1] : defaultName;
        }catch (e) {
          console.error('文件名异常:', e);
          return defaultName;
        }
      },
      triggerFileDownload(blob, fileName) {
        try {
          const link = document.createElement('a');
          if (!blob.size) throw new Error('Empty file content');
          
          link.href = URL.createObjectURL(blob);
          link.download = fileName;
          document.body.appendChild(link);
          link.click();
          
          setTimeout(() => {
              URL.revokeObjectURL(link.href);
              link.remove();
          }, 100);
        } catch (error) {
            console.error('Download failed:', error);
            this.$message.error('文件下载失败：无效的文件内容');
        }
      },
      handleAddOrEdit(row){
        if (row.id){
          //编辑模式
          this.form = JSON.parse(JSON.stringify(row)) //弹窗带出来信息,浅拷贝是内存指向，深拷贝是开区域赋值
          // 确保 autoCode 有默认值
          if (this.form.autoCode === null) {
            this.form.autoCode = false;
          }
          if (!this.form.deWeightType) {
            this.form.deWeightType = 'project';
          }
          //需要把upload组件的fileList带出来
          if (this.form.fileListJson){
            this.fileList = this.form.fileListJson
          }else {
            this.fileList = []
          }
          //接收人
          this.selectVal = this.form.recipient
          this.selectName = this.form.recipientName
        }else {
          this.form = {
            autoCode: true,
            deWeightType: 'project'
          } //新增数据时清空表单
          this.fileList = [] //清空上传组件
        }
        this.formVisible = true //打开弹窗
        //初始化选择框
        // this.initRecipientSelect();
      },
      //单个删除
      handleDel(row){
        this.$confirm('确认要删除【'+row.workName+'】这条任务的记录吗？','确认删除',{type: "warning"}).then(ok => {
          this.$request.delete('/bom/delete/'+row.id).then(res => {
            if (res.code == '200'){
              this.$message.success("删除成功")
            }else {
              this.$message.error("删除失败："+res.msg)
            }
            this.load(this.pageNum) //刷新表格
          })
        }).catch(() => {}) //catch捕捉先不做处理
      },
      //批量删除之前--多选框，参数是选中的行对象
      handleSelectionChange(rows){
        this.ids = rows.map(row => row.id) //流和表达式，将行id提取出来存入数组
      },
      //批量删除
      handleDelBatch(){
        //校验：点击批量删除按钮之前，未选择多选框时提示
        if (!this.ids.length){
          this.$message.warning('请选择需要批量删除的任务记录！')
          return
        }
        this.$confirm('你确认批量删除【'+this.ids+'】这些任务的记录吗？','确认删除',{type: "warning"}).then(ok => {
          this.$request.delete('/bom/deleteBatch',{data: this.ids}).then(res => {
            if (res.code == '200'){
              this.$message.success("批量删除成功")
            }else {
              this.$message.error("批量删除失败:"+res.msg)
            }
            this.load(this.pageNum) //刷新表格
          })
        }).catch(() => {}) //catch捕捉先不做处理
      },
      //关闭弹窗后的回调方法
      closeFormVisible(){
        //清除rules提示信息
        if (this.$refs.formRef) {
          this.$refs.formRef.clearValidate()
        }
        // 销毁编辑器实例
        if (this.editor) {
          this.editor.destroy()
          this.editor = null
        }
      },
      //关闭ws弹窗后的回调方法
      closeWsDialogVisible(){
        //清除rules提示信息
        // this.$refs.formRef.clearValidate()
        //销毁富文本编辑器对象
        this.editor.destroy()
        this.editor = null
      },
      //上传文件组件，文件改变事件(添加文件ready，上传成功success/失败error，都会被调用，至少2次)
      handleChange(file, fileList){
        if (file.status !== 'ready') return; //文件未准备好是减少调用次数
      },
      //上传文件组件，文件预览
      handlePreview(file){
        const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tiff', 'webp'];

        let fileExtensionMatch  = file.response.data.fileName.match(/\.([^\.]+)$/)
        let fileExtension = fileExtensionMatch ? fileExtensionMatch[1] : ''  //文件扩展名
        if (imageExtensions.includes(fileExtension.toLowerCase())) {
          this.$message.warning('预览图片功能正在开发中……')
        }else {
          this.$message.warning('选中的文件是非图片，暂不支持预览')
        }

      },
      //上传文件组件，限制附件个数
      handleExceed(files, fileList){
        this.$message.warning(`当前限制选择 30 个附件，本次选择了 ${files.length} 个文件，共选择了 ${files.length + fileList.length} 个文件`);
      },
      //上传文件组件，成功后记录文件名和文件路径
      handleOnSuccess(response, file, fileList){
        this.fileList = fileList;
        //改变附件名称
        this.form.attachName = this.fileList.map(file => file.name).join(',')
        if(this.form.attachName.length > 100){
          this.form.attachName = this.form.attachName.substring(0,97) + '...'
        }
      },
      //上传组件，移除元素之前
      beforeRemoved(file,fileList){
        if (file && file.status === 'success'){
          this.$confirm(`确定移除【${file.name}】吗？`).then(()=>{
            //刷新一下全局变量
            this.fileList = fileList
            // console.log(fileList)
            //确认移除,改变附件名称
            this.form.attachName = this.fileList.map(file => file.name).join(',')
            return true
          }).catch(() => {
            //取消或关闭
            return false
          })
        }
      },
      //上传文件之前
      beforeUpload(file){
        //if (this.form.workName === undefined || this.form.workName === null){
        //  this.$message.error("请选择任务名称后再上传文件")
        //  return false
        //}
        const workName = this.form.workName;
        const fileType = file.type;
        if (workName === '合并PDF' || workName === '拆分PDF'){
          const isPDF = fileType === 'application/pdf';
          if (!isPDF){
            this.$message.error('操作PDF任务时只能上传PDF文件！')
            return false
          }
        }

        let size = file.size;
        let maxSize = 100 * 1024 * 1024;
        if (size > maxSize){
          // this.$message.warning("附件大小不能超过100MB")
          // return false
          // this.$refs.uploader.abort() //终止上传
          return this.$confirm('当前附件【'+file.name+'】文件大小超出100MB安全范围，继续上传可能会出错，请确认是否继续上传？','警告提示')
        }
      },
      //保存/修改
      save() {
        // 检查文件列表是否为空
        if (this.fileList.length === 0) {
              this.$message.warning('请先选择文件或等待文件传输完成后再提交');
              return;
        }
        //校验表单
        // 根据数组form名称找到这个校验对象
        // this.$refs[formName].validate((valid) => {
        this.$refs.formRef.validate((valid) => {
          if (valid) {
            this.saveLoad = true;
            //接收人赋值
            this.form.recipient = this.selectVal;
            this.form.fileList = this.fileList;
            this.sendApi();
          } else {
            this.$message.warning('表单校验未通过')
            return false
          }
        })
      },
      //向接口发送
      sendApi(){
        this.$request({
          //有id就是编辑
          method: this.form.id ? 'PUT' : 'POST',
          data: this.form,
          url: this.form.id ? '/bom/update' : '/bom/save',
        }).then((res) => {
          if (res.code === '200') {
            //保存成功
            this.$message.success('提交成功，请等待处理结果')
            this.formVisible = false
            this.load(this.pageNum)
          } else {
            this.$message.error(res.msg) //错误信息
            return false
          }
          //无论是否保存成功，都要把fileList清空
          this.fileList = []
          this.saveLoad = false;
        }).finally(() => {
          this.saveLoad = false;
        });
      },
      getStatusText(status) {
        switch (status) {
          case 0:
            return '未处理';
          case 1:
            return '正在加工';
          case 2:
            return '加工成功';
          case 3:
            return '加工失败';
          case 4:
            return '取消加工';
          default:
            return '未知状态';
        }
      },
      getStatusType(status) {
        switch (status) {
          case 0:
            return 'info';
          case 1:
            return 'warning';
          case 2:
            return 'success';
          case 3:
            return 'danger';
          case 4:
            return 'danger';
          default:
            return 'info'; // 默认没有特殊样式
        }
      },
      // //清空聊天消息
      // cleanChatMsg(){
      //   // 检查是否选择了会话人
      //   if(!this.wsForm.wsOppo || this.wsForm.wsOppo === ''){
      //     this.$message.warning('请先选择会话人');
      //     return;
      //   }
      //   this.$confirm('确认要清空与该用户的聊天记录吗？', '确认清空', {
      //     type: 'warning'
      //   }).then(() => {
      //     this.$request.delete('/chat/cleanChatRecords', {
      //       params: {
      //         userId: this.user.id,
      //         oppoId: this.wsForm.wsOppo
      //       }
      //     }).then(res => {
      //       if(res.code === '200'){
      //         this.msgList = [];
      //         this.$message.success('聊天记录已清空');
      //       }else{
      //         this.$message.error('清空失败：' + res.msg);
      //       }
      //     }).catch(error => {
      //       this.$message.error('网络错误，清空失败');
      //       console.error('清空聊天记录失败:', error);
      //     });
      //   }).catch(() => {
      //     // 取消操作
      //   });
      // },
      // 滚动到底部方法
      scrollToBottom() {
        // 延迟执行，确保DOM已经更新
        setTimeout(() => {
          if (this.$refs.messageContainer) {
            this.$refs.messageContainer.scrollTop = this.$refs.messageContainer.scrollHeight;
          }
        }, 100);
      },
      sendInitMsg(){
        const websocket = this.getWebsocket();
        const msg = {
            type: 'init',
            content: this.user.id+'号用户正在连接……',
            uid: this.user.id,
            oppo: '0'
        };
        websocket.send(JSON.stringify(msg));
        // this.msgList.push({
        //   type: "me", 
        //   content: msg.content, 
        //   time: new Date().toLocaleString() 
        // });
      },
      // 站内信发送消息
      // sendChatMsg() {
      //   // 确保富文本编辑器有内容
      //   if (this.editor) {
      //     this.wsForm.content = this.editor.txt.html();
      //   }
      //   // 表单验证
      //   this.$refs.wsForm.validate((valid) => {
      //     if (valid) {
      //       const websocket = this.getWebsocket();
      //       if (!websocket) {
      //         this.$message.error('WebSocket未连接');
      //         return;
      //       }
      //       if (websocket.readyState !== WebSocket.OPEN) {
      //           this.$message.warning('WebSocket连接未准备好，当前状态：' + websocket.readyState);
      //           return;
      //       }
      //       // 处理特殊字符，避免数据库存储错误
      //       let processedContent = this.wsForm.content;
      //       try {
      //         // 可以在这里添加更多的处理逻辑
      //         processedContent = processedContent.replace(/[\xF0-\xF7][\x80-\xBF]{3}/g, ''); // 移除表情符号
      //         // this.$message.warning("表情符号比较特殊，对方可能收不到表情符号。")
      //       } catch (e) {
      //         console.error('处理内容时出错:', e);
      //       }
      //       const msg = {
      //           type: 'chat',
      //           content: processedContent,
      //           uid: this.user.id,
      //           oppo: this.wsForm.wsOppo
      //       };
      //       websocket.send(JSON.stringify(msg));
      //       this.msgList.push({
      //         type: "me", 
      //         content: msg.content, 
      //         time: new Date().toLocaleString() 
      //       });
      //       if (this.msgList.length > 100) this.msgList.shift(); //最多保留100条消息
      //       // 清空表单
      //       this.wsForm.content = '';
      //       if (this.editor) {
      //         this.editor.txt.clear();
      //       }
      //       this.scrollToBottom();
      //     }
      //   });
      // },
      //初始化用户选择框
      initUsersOptions(){
        this.$request({
          method: 'GET',
          url: '/user/getSelect',
        }).then((res) => {
          if (res.code === '200') {
            this.userOptions = res.data;
          } else {
            this.$message.error(res.msg) //错误信息
            return false
          }
        })
      },
      //是否是归属人,并且状态是加工完成
      isRecipient(row){
        return (row.creator === this.user.id) && (row.state === 2);
      },

      //下载导入模板
      // downloadExcelTemplate() {
      //     window.open(
      //         this.$apiUrl +
      //             '/file/download/excel_muban.xlsx'
      //     )
      // },

      //下载导入模板
      downloadExcelTemplate() {
        try {
          // 假设下载接口的 URL
          const url = `${this.$apiUrl}/file/download/三谷工具库-Excel清单筛选模板.xlsx`;
          const params = {
            token: this.user.token
          };
          const fullUrl = `${url}?${new URLSearchParams(params).toString()}`;

          // 使用 fetch 发起请求
          fetch(fullUrl, {
            headers: {
              'Content-Type': 'application/json',
              'Authorization': `Bearer ${this.user.token}`
            }
          })
          .then(response => {
            if (!response.ok) {
              throw new Error(`HTTP 错误! 状态码: ${response.status}`);
            }
            // 从响应头中获取文件名
            const disposition = response.headers.get('Content-Disposition');
            let fileName = 'Excel清单筛选模板.xlsx';
            if (disposition) {
              const matches = disposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
              if (matches != null && matches[1]) {
                fileName = matches[1].replace(/['"]/g, '');
              }
            }
            return response.blob().then(blob => ({ blob, fileName }));
          })
          .then(({ blob, fileName }) => {
            // 创建一个隐藏的 a 标签用于触发下载
            const link = document.createElement('a');
            link.href = window.URL.createObjectURL(blob);
            // 设置下载的文件名
            link.setAttribute('download', fileName);
            document.body.appendChild(link);
            link.click();
            // 清理操作
            document.body.removeChild(link);
            window.URL.revokeObjectURL(link.href);
          })
          .catch(error => {
            console.error('文件下载失败:', error);
            this.$message.error('Excel 模板下载失败，请联系管理员');
          });
        } catch (error) {
          console.error('文件下载过程中出现错误:', error);
          this.$message.error('Excel 模板下载失败，请联系管理员');
        }
      },

      // 添加 handleDrop 方法
      handleDrop(event) {
        event.preventDefault();
        // 可以根据需求添加文件处理逻辑
        console.log('文件已拖拽放下', event.dataTransfer.files);
      },
      // 显示WS-Chat弹窗
      showWsChatDialog() {
        this.wsDialogVisible = true;
        // this.wsInit();
        // this.$nextTick(() => {
        //   //新建富文本对象,#editor是内容div的id
        //   this.editor = new E(`#editor`)
        //   //代码高亮插件
        //   this.editor.highlight = hljs

        //   //图片上传服务器接口
        //   // this.editor.config.uploadImgServer = '/file/editor/upload'  //默认前缀是前端服务地址
        //   this.editor.config.uploadImgServer = this.editorFileUpload
        //   //file与接口参数名一致
        //   this.editor.config.uploadFileName = 'file'
        //   //传令牌鉴权，放在Img头里
        //   this.editor.config.uploadImgHeaders = {
        //     token: this.user.token
        //   }
        //   //传参type，可从url，也可从参数函数传
        //   this.editor.config.uploadImgParams = {
        //     // token: xxx
        //     type: "img"
        //   }

        //   // 配置上传成功后的自定义插入逻辑（解决src非字符串问题）
        //   this.editor.config.uploadImgHooks = {
        //     customInsert: (insertImg, result, editor) => {
        //       console.log(result);
        //       if (result.errno === 0 && result.data?.length > 0) {
        //         // 根据实际返回结构调整：data是数组，取第一个元素的url对象中的fileUrl
        //         const imgUrl = result.data[0].url.fileUrl?.trim(); // 去除可能的前后空格
        //         if (typeof imgUrl === 'string') {
        //           insertImg(imgUrl);
        //         } else {
        //           this.$message.error('图片URL格式不正确');
        //         }
        //       } else {
        //         this.$message.error('图片上传失败：' + (result.msg || '无有效数据'));
        //       }
        //     }
        //   }

        //   //上传视频
        //   this.editor.config.uploadVideoServer = this.editorFileUpload
        //   this.editor.config.uploadVideoName = 'file'
        //   this.editor.config.uploadVideoHeaders = {
        //     token: this.user.token
        //   }
        //   this.editor.config.uploadVideoParams = {
        //     type: "video"
        //   }
        //   //正式创建
        //   this.editor.create()
        // })
      },
      // 显示自动编码弹窗
      showAutocodeDialog() {
        this.autocodeVisible = true;
        this.$request.get('/autocode/clearCount').then(res => {
          if (res.code === '200') {
            this.$message.success(res.data || '成功连接服务并清除缓存');
          } else {
            this.$message.error(res.message || '缓存清除失败');
          }
        })
        .catch(error => {
          this.$message.error('请求失败: ' + error.message);
        });
      }
    }
  }
</script>

<style scoped>
  .input {
    width: 260px;
    margin: 10px;
  }
</style>